package cloudspace.vm.javassist;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * The Class Macro. This abstract class represents a macro defined in a
 * configuration file. Note, all parameters in the macro body must be prefixed
 * by a %.
 * 
 * define <Macro Name>(<Parameters>) <Macro Body>
 * 
 * The macros are then used in other command bodies like so:
 * 
 * %<MacroName>(<Parameters>)
 * 
 * 
 * 
 */
public abstract class Macro
{

    public class MalformedMacroParametersException
                    extends
                    MalformedMacroException
    {

        /**
		 * 
		 */
        private static final long serialVersionUID = 1L;


        public MalformedMacroParametersException( String macro )
        {
            super( "Unable to parse parameters in " + macro );
        }

    }

    /** The macro. */
    String macro;

    /** The macro name. */
    String macroName;

    /** The macro params. */
    List<String> macroParams;

    /** The macro name pat. */
    Pattern macroNamePat;

    /** The param pat. */
    Pattern paramPat;


    /**
     * Instantiates a new macro. This constructor allows a user to define a
     * custom patter for parameters and names.
     * 
     * @param userMacro
     *            the name of the user passed macro
     * @param namePattern
     *            the pattern used to identify the name
     * @param paramPattern
     *            the the pattern used to identify the parameters.
     * 
     * @throws MalformedMacroParametersException
     *             the malformed macro parameters exception
     */
    public Macro( String userMacro, String namePattern, String paramPattern )
        throws MalformedMacroException
    {
        macroNamePat = Pattern.compile( namePattern );
        paramPat = Pattern.compile( paramPattern );
        macro = userMacro;
        macroName = getMacroName( macro );
        macroParams = getParameters( macro );
    }


    /**
     * Gets the number of parameters this macro requries.
     * 
     * @return the number of params
     */
    public int getNumberOfParams()
    {
        return macroParams.size();
    }


    /**
     * Gets the macro name.
     * 
     * @return the macro name
     */
    public String getMacroName()
    {
        return macroName;
    }


    /**
     * Gets the parameter iterator.
     * 
     * @return the parameter iterator
     */
    public Iterator<String> getParameterIterator()
    {
        return macroParams.iterator();
    }


    private String getMacroName( String macro ) throws MalformedMacroException
    {
        String mName = "";

        Matcher macroNameMatcher = macroNamePat.matcher( macro );
        // Matcher parameterMatcher = parameter.matcher(macro);
        if ( macroNameMatcher.find() )
        {
            mName = macroNameMatcher.group().substring( 0,
                macroNameMatcher.end() - 1 );
            macro = macro.substring( macroNameMatcher.end() );
            // System.out.println(m.group());
        }
        else
        {
            System.err.println( "Malformed Macro" );
            throw new MalformedMacroException( "Unable to find macro name in:  "
                + macro );
        }
        return mName;
    }


    private List<String> getParameters( String macro )
        throws MalformedMacroParametersException
    {
        List<String> parameters = new ArrayList<String>();
        // Pattern paramPat = Pattern.compile("\\(.*\\)\\s*\\{");
        Matcher parameterSectionMatcher = paramPat.matcher( macro );
        String parameterSection = "";
        if ( parameterSectionMatcher.find() )
        {
            parameterSection = parameterSectionMatcher.group();
        }
        else
        {
            throw new MalformedMacroParametersException( macro );
        }
        // Pattern parameter = Pattern.compile("[^(%),.\\s]+[,|\\)]");
        parameterSection = parameterSection.trim().substring( 1 );
        Pattern parameter = Pattern.compile( "[^\\s]+\\s*," );
        Matcher parameterMatcher = parameter.matcher( parameterSection );

        while ( parameterMatcher.find() )
        {
            String tempParam = parameterMatcher.group().trim();
            tempParam = tempParam.substring( 0, tempParam.length() - 1 );
            parameterSection = parameterSection.substring( parameterMatcher.end() )
                .trim();
            parameterMatcher = parameter.matcher( parameterSection );
            parameters.add( tempParam );
        }
        Pattern lastParam = Pattern.compile( "[^\\s]+\\s*\\)" );
        Matcher lastParamMatch = lastParam.matcher( parameterSection );

        if ( lastParamMatch.find() )
        {
            String lParam = lastParamMatch.group();
            lParam = lParam.trim().substring( 0, lParam.length() - 1 );
            lParam = lParam.trim();
            parameters.add( lParam );
        }
        return parameters;
    }
}
